﻿/*
 * Copyright (c) http://DesignBased.NET 
 * Author: http://t.163.com/Jeek 
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the 
 * Software  * without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 
 * Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
 * A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 * 
 */

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Linq;
using T163.Open.Api.WP7.Model;

namespace T163.Open.Api.WP7
{
    internal static class OAuthUtility
    {
        private static string GetTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        private static string GetNonce()
        {
            return Guid.NewGuid().ToString();
        }

        internal static string GenerateAuthorizationHeader(
            string realm,
            string url, 
            string callback,
            HttpMethodType type,
            Consumer consumer,
            TokenBase token, 
            string verifier,
            IEnumerable<Parameter> args)
        {
            List<Parameter> authorizationHeaderParameters = new List<Parameter>
            {
                 new Parameter() { Key = "oauth_consumer_key", Value = consumer.ConsumerKey },
                 new Parameter() { Key = "oauth_signature_method", Value = "HMAC-SHA1" },
                 new Parameter() { Key = "oauth_timestamp", Value = OAuthUtility.GetTimeStamp() },
                 new Parameter() { Key = "oauth_nonce", Value = OAuthUtility.GetNonce() },
                 new Parameter() { Key = "oauth_version", Value = "1.0" }
            };

            if (!string.IsNullOrEmpty(callback))
            {
                authorizationHeaderParameters.Add(new Parameter() { Key = "oauth_callback", Value = callback });
            }

            if (token != null)
            {
                authorizationHeaderParameters.Add(new Parameter() { Key = "oauth_token", Value = token.Token });
            }

            if (!string.IsNullOrEmpty(verifier))
            {
                authorizationHeaderParameters.Add(new Parameter() { Key = "oauth_verifier", Value = verifier });
            }

            StringBuilder headerBase = new StringBuilder("OAuth ");

            if (!string.IsNullOrEmpty(realm))
            {
                headerBase = headerBase.Append(string.Format("realm=\"{0}\",", realm));
            }

            headerBase.Append(string.Join(",",
                authorizationHeaderParameters.Select(
                (p) => { return p.Key + "=" + "\"" + p.Value + "\""; }
                ).ToArray()));

            headerBase.Append("," + "oauth_signature=\"" + GenerateSignature(consumer, token, type, url, authorizationHeaderParameters, args) + "\"");

            return headerBase.ToString();
        }

        private static string GenerateSignature(
            Consumer c, TokenBase token, HttpMethodType type,
            string url,
            IEnumerable<Parameter> oAuthParameters,
            IEnumerable<Parameter> parameters)
        {
            string key = c.ConsumerSecret.OAuthPercentEncode() + "&" + (token == null ? string.Empty : token.TokenSecret.OAuthPercentEncode());

            string method;

            switch (type)
            {
                case HttpMethodType.Get:
                    method = "GET";
                    break;
                case HttpMethodType.Post:
                    method = "POST";
                    break;
                default:
                    method = null;
                    break;
            }

            string baseUrl = url.OAuthPercentEncode();

            IEnumerable<Parameter> allParas = oAuthParameters.Union(parameters);

            var sortedParas = from p in allParas
                              orderby p.Key, p.Value ascending
                              select p.Key + "=" + p.Value.OAuthPercentEncode();

            string paras = string.Join("&", sortedParas.ToArray());

            string signatureBaseString = method + "&" + baseUrl + "&" + paras.OAuthPercentEncode();

            byte[] hash;

            using (HMACSHA1 pro = new HMACSHA1())
            {
                pro.Key = Encoding.UTF8.GetBytes(key);

                hash = pro.ComputeHash(Encoding.UTF8.GetBytes(signatureBaseString));
            }

            return Convert.ToBase64String(hash).OAuthPercentEncode();
        }
    }
}
